/*
 * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package cn.bbstone.pisces2.server;

import cn.bbstone.pisces2.comm.Const;
import cn.bbstone.pisces2.config.Config;
import cn.bbstone.pisces2.config.ConfigFactory;
import cn.bbstone.pisces2.config.ConfigModel;
import cn.bbstone.pisces2.listener.*;
import cn.bbstone.pisces2.server.base.ServerCmdRegister;
import cn.bbstone.pisces2.util.CtxUtil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ServiceLoader;

/**
 * Server that accept the path of a file an echo back its content.
 */
public final class FileServer {
    private static Logger log = LoggerFactory.getLogger(FileServer.class);

    public static void startup(IListenerRegister listenerRegister) {
        // init configs
        if (CtxUtil.getConfigModel() == null) {
            ConfigModel configModel = ConfigFactory.loadConfigs(Const.SERVER);
            CtxUtil.setConfigModel(configModel);
        }

        // register OpListners
        BaseListenerRegister.init(listenerRegister);

        // Configure the server.
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerCmdRegister.init();

            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 100)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline p = ch.pipeline();
                            // ---- server outbound

                            // ---- server inbound
                            /**
                             * @param maxFrameLength 解码时，处理每个帧数据的最大长度
                             * @param lengthFieldOffset 该帧数据中，存放该帧数据的长度的数据的起始位置
                             * @param lengthFieldLength 记录该帧数据长度的字段本身的长度
                             * @param lengthAdjustment 修改帧数据长度字段中定义的值，可以为负数
                             * @param initialBytesToStrip 解析的时候需要跳过的字节数
                             *
                             *  maxFrameLength, 1M bytes
                             *
                             */
                            p.addLast(new LengthFieldBasedFrameDecoder(1024 * 1024, 56, 2, 0, 0));
                            // server customize handler
                            p.addLast(new FileServerHandler());

                        }
                    });

            // Start the server.
            ChannelFuture f = b.bind(CtxUtil.getConfigModel().getServerPort()).sync();
            f.addListener((ChannelFutureListener) future -> {
                OpUtil.execListener(OpEnum.s4_done_startup_server, null);
            });
            log.info("server is running...");
            // Wait until the server socket is closed.
            f.channel().closeFuture().sync(); // thread blocked
        } catch (Exception e) {
            log.error("server startup error.", e);
        } finally {
            // Shut down all event loops to terminate all threads.
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    private void addListeners() {

    }
}
